#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>

#include "best_lock.h"
#define NUM_THREADS    4 

volatile int sum = 0;

//the mutex for TAS (Test And Set)
volatile int tas_mutex = TAS_UNLOCKED;

/*
struct {
  long secs, usecs;
} times_t;
//times [x][0] = start time of thread x
//times [x][1] = end time of thread x
struct times_t times [NUM_THREADS][2];
*/

double times [NUM_THREADS][2];

void *new_thread(void *thread_info)
{
  int i;
  struct timeval tv;
  struct timezone tz;
  time_t start_secs, end_secs;
  suseconds_t start_usecs, end_usecs;
  double time_spent = 0.0;
  
  int tid = (int) thread_info;

  // Bind this thread to it's own processor.
  int mask = 1 << (tid % 8); 
  sched_setaffinity(0, 1, &mask);

  gettimeofday (&tv, &tz);
  times [tid][0] = (tv.tv_sec - 1134000000) + (double)((double) tv.tv_usec / 1000000);
  //times [tid][0].secs = tv.tv_sec;
  //times [tid][0].usecs = tv.tv_usec;
  //start_secs = tv.tv_sec;
  //start_usecs = tv.tv_usec;
  for (i = 0; i < 1000000; i++)
  {
    //printf("thread %d entering critical section\n", tid); fflush(0);
    best_mutex_lock (&tas_mutex);
    sum++;
    best_mutex_unlock (&tas_mutex);
    //printf("thread %d leaving critical section\n", tid); fflush(0);
  }
  gettimeofday (&tv, &tz);
  //times [tid][1].secs = tv.tv_sec;
  //times [tid][1].usecs = tv.tv_usec;
  times [tid][1] = (tv.tv_sec - 1134000000) + (double)((double) tv.tv_usec / 1000000);
  
  /*
  end_secs = tv.tv_sec;
  end_usecs = tv.tv_usec;
  if (end_usecs < start_usecs) {
     times [tid] = ((double)(end_secs - start_secs - 1)) + (double)(((double)(1000000 + end_usecs - start_usecs)) / 1000000);
     printf ("written %g\n", times [tid]);
  }
  else {
     times [tid] = ((double)(end_secs - start_secs)) + (double)(((double)(end_usecs - start_usecs)) / 1000000);
     printf ("written %g\n", times [tid]);
  }
  */
}

int main (int argc, char* argv[])
{
  int i, result;
  double total_time = 0.0, min_start_time = 0.0, max_end_time = 0.0;
  pthread_t threads[NUM_THREADS - 1];


  for (i = 1; i < NUM_THREADS; i++)
  {
    if (result = pthread_create(&threads[i], NULL, new_thread, (void *) i))
    {
      printf("ERROR; return code from pthread_create() is: %d\n", result);
      exit(-1);
    }
  }

  // Call new_thread function without starting a new thread so that this thread does some
  // work too.
  new_thread((void *) 0);

  // Wait for all threads to exit.
  for (i = 1; i < NUM_THREADS; i++)
    pthread_join(threads[i], NULL);

  printf("sum is: %d\n", sum);
  
  //find the min start time and the max end time and subtract them
  //min_start_time_sec = times [0][0].secs;
  //min_start_time_usec = times [0][0].usecs;
  //max_end_time = times [0][1].secs;
  //max_end_time = times [0][1].usecs;
  min_start_time = times [0][0];
  max_end_time = times [0][1];
  
 for (i = 0; i < NUM_THREADS; i ++) {
     printf ("start_time = %18.12g, end_time = %18.12g\n", times [i][0], times [i][1]);
     if (times [i][0] < min_start_time) 
        min_start_time = times [i][0];
     if (times [i][1] > max_end_time) 
        max_end_time = times [i][1];
  }
  
  printf ("min_start_time=%18.12g, max_end_time=%18.12g, total_time is %18.12g\n", min_start_time, 
          max_end_time, max_end_time - min_start_time);

  char c;
  printf("Press enter...\n");
  scanf("%c",&c);

}

